﻿using System;
using System.Collections.Generic;

namespace Jacobi.ComponentModel.Decomposition
{
    /// <summary>
    /// A base class for a concrete filter implementation.
    /// </summary>
    /// <typeparam name="Tin">The data type of the incoming data items.</typeparam>
    /// <typeparam name="Tout">The data type of the outgoing data items.</typeparam>
    public abstract class FilterBase<Tin, Tout> : IFilter<Tin, Tout>
    {
        /// <summary>
        /// Derived ctor.
        /// </summary>
        protected FilterBase()
        {
            Inputs = new List<IConsumerPort<Tin>>();
            Outputs = new List<IProducerPort<Tout>>();
        }

        /// <summary>
        /// Gets a list with pipe consumer ports.
        /// </summary>
        public IList<IConsumerPort<Tin>> Inputs { get; private set; }

        /// <summary>
        /// Gets a list with pipe producer ports.
        /// </summary>
        public IList<IProducerPort<Tout>> Outputs { get; private set; }

        /// <summary>
        /// Reads one data item from all <see cref="Inputs"/> and calls the <see cref="ProcessInternal"/> method.
        /// Then the output data items are put onto the <see cref="Outputs"/>.
        /// </summary>
        public virtual void Process()
        {
            List<Tin> inputs = new List<Tin>();

            // NOTE: these ports are opened sequentially.
            // this means that when one block because no data is available
            // other ports are kept waiting potentially 'waisting' one slot.
            foreach (IConsumerPort<Tin> inPort in Inputs)
            {
                inputs.Add(inPort.Read());
            }

            IList<Tout> outputs = ProcessInternal(inputs);

            int index = 0;
            foreach (IProducerPort<Tout> outPort in Outputs)
            {
                outPort.Write(outputs[index]);
                index++;
            }
        }

        /// <summary>
        /// The derived class implements this method to process the inputs and yield outputs.
        /// </summary>
        /// <param name="inputs">A list of data item values to process.</param>
        /// <returns>Returns a list of ouput data items. Never returns null.</returns>
        protected abstract IList<Tout> ProcessInternal(IList<Tin> inputs);
    }
}
